package handler

import (
	"encoding/json"
	mydb "filestore-server/db"
	"filestore-server/meta"
	"filestore-server/util"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"
)

// 保存路径
const SaveFilePath = "/temp/"

// UploadHandler 上传接口
func UploadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	if r.Method == "GET" {
		// 返回上传html页面
		data, err := ioutil.ReadFile("./static/view/index.html")
		if err != nil {
			log.Printf("UploadHandler -> {%s}", err)
			_, _ = io.WriteString(w, "服务器错误")
			return
		}
		 _, _ = io.WriteString(w, string(data))
	} else if r.Method == "POST" {
		username := r.Form.Get("username")

		// 接收文件流
		file, fileHeader, err := r.FormFile("file")
		if err != nil {
			log.Printf("UploadHandler: 上传文件出错 -> {%s}", err)
			_, _ = io.WriteString(w, "上传文件出错！")
			return
		}
		defer file.Close()

		// 装载上传上来的文件元信息
		fileMate := meta.FileMeta{
			FileName: fileHeader.Filename,
			Location: SaveFilePath + fileHeader.Filename,
			UploadAt: time.Now().Format("2006-01-01 15:04:05"),
		}

		// 解析成 JSON 数据
		headerByte, _ := json.Marshal(fileHeader.Header)
		log.Printf("当前文件：Filename - >{%s}, Size -> {%v}, FileHeader -> {%s}", fileHeader.Filename, fileHeader.Size, string(headerByte))

		newFile, err := os.Create(fileMate.Location)
		if err != nil {
			log.Printf("UploadHandler: 创建文件失败！")
			_, _ = io.WriteString(w, "服务器错误！")
			return
		}
		defer newFile.Close()

		// 复制文件到目标目录
		// 获取返回的文件大小 和 错误信息
		fileMate.FileSize, err = io.Copy(newFile, file)
		if err != nil {
			log.Printf("UploadHandler: 文件复制失败！ -> {%s}", err)
			_, _ = io.WriteString(w, "服务器错误！")
			return
		}

		// 把文件的读取指针移动到初始位置
		// 参数解释 :
		//			offset : 开始的偏移量，也就是代表需要移动偏移的字节数
		//			whence : (可选)，默认值为 0.给offset参数一个定义，表示要从哪个位置开始偏移；
		//			   		0代表从文件开头开始算起，1代表从当前位置开始算起，2代表从文件末尾算起
		newFile.Seek(0, 0)
		// 获取文件的 Sha1 值
		fileMate.FileSha1 = util.FileSha1(newFile)
		// meta.UpdateFileMeta(fileMate)
		meta.UpdateFileMetaDB(fileMate)

		// 更新用户文件表记录
		suc := mydb.OnUserFileUploadFinished(username, fileMate.FileSha1, fileMate.FileName, fileMate.FileSize)
		if suc {
			// 成功响应

			// 重定向到这个请求
			http.Redirect(w, r, "/file/upload/suc", http.StatusFound)
		} else {
			log.Fatal("插入用户文件关联关系失败！")
			w.Write([]byte("upload Failed"))
		}
	}
}

// TryFastUploadHandler 尝试秒传接口
func TryFastUploadHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	username := r.Form.Get("username")
	filehash := r.Form.Get("filehash")
	filename := r.Form.Get("filename")
	//filesize, _ := strconv.Atoi(r.Form.Get("filesize"))

	// 判断文件是否在
	fileMateResp, err := mydb.GetFileMeta(filehash)
	if err != nil {
		log.Fatalf("获取文件信息失败！err: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

    // 查询不到返回秒传失败
    if !fileMateResp.Suc {
		log.Fatalf("未查询到文件记录！")
		resp := &util.RespMsg{
			Code: -1,
			Msg:  "秒传失败，请访问普通上传接口",
		}
		w.Write(resp.JSONBytes())
		return
	}

	// 查询到则与用户数据关联
	fbool := mydb.OnUserFileUploadFinished(username, filehash, filename, fileMateResp.FileSize.Int64)
	if fbool {
		resp := util.RespMsg{
			Code: 0,
			Msg:  "秒传成功",
		}
		w.Write(resp.JSONBytes())
		return
	}

	resp := util.RespMsg{
		Code: 0,
		Msg:  "秒传失败，请稍后重试",
	}
	w.Write(resp.JSONBytes())
}

// UploadSuccessHandler 响应
func UploadSuccessHandler(w http.ResponseWriter, r *http.Request) {
	_, _ = io.WriteString(w, "Upload finished！")
}

// GetFileMetaHandler 获取 文件的Meta信息
func GetFileMetaHandler(w http.ResponseWriter, r *http.Request) {
	// 解析Form参数
	r.ParseForm()

	// 获取提交的参数
	filehash := r.Form["filehash"][0]

	// fMeta := meta.GetFileMeta(filehash)
	fMeta, err := meta.GetFileMetaDB(filehash)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 转换JSON
	data, err := json.Marshal(fMeta)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 响应数据
	w.Write(data)
}

// GetFileListHandler 获取文件列表
func GetFileListHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	username := r.Form.Get("username")
	limitCnt, _ := strconv.Atoi(r.Form.Get("limit"))

	// 查询文件
	//userFiles, err := mydb.GetByUserFileList(username, limitCnt)

	userFiles, err := mydb.QueryUserFileMetas(username, limitCnt)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Fatalf("用户文件查询错误！err:%v", err)
		return
	}

	resp := util.RespMsg{
		Code: 0,
		Msg:  "OK",
		Data: userFiles,
	}

	w.WriteHeader(http.StatusOK)
	w.Write(resp.JSONBytes())
}

// DownloadHandler 下载文件
func DownloadHandler(w http.ResponseWriter, r *http.Request) {
	// 解析Form参数
	r.ParseForm()

	fshal := r.Form.Get("filehash")
	fmate := meta.GetFileMeta(fshal)

	// 打开这个文件
	file, err := os.Open(fmate.Location)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer file.Close()

	// 如果文件比较大的话，建议分段读取，然后返回给浏览器
	// 这里文件比较小，所以就直接一次性全部读取到内存 ioutil.ReadAll(file)

	// 读取这个文件流
	data, err := ioutil.ReadAll(file)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// 设置请求头信息，让浏览器可以执行下载
	w.Header().Set("Content-Type", "application/octect-stream")
	w.Header().Set("Content-Descrption", "attachment;filename=\"" + fmate.FileName + "\"")
	w.Write(data)
}

// FileMetaUploadHandler9 更新元信息接口(重名)
func FileMetaUploadHandler(w http.ResponseWriter, r *http.Request) {
	// 解析Form数据
	r.ParseForm()

	opType := r.Form.Get("op")
	fileMeta := r.Form.Get("filehash")
	newFileName := r.Form.Get("filename")

	// 只支持重命名操作
	if opType != "0" {
		w.WriteHeader(http.StatusForbidden)
		return
	}
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	curFileMeta := meta.GetFileMeta(fileMeta)
	curFileMeta.FileName = newFileName
	// 更新文件信息
	meta.UpdateFileMeta(curFileMeta)


	data, err := json.Marshal(curFileMeta)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(data)
}

// FileDeleteHandler 删除文件
func FileDeleteHandler(w http.ResponseWriter, r *http.Request) {
	// 解析Form数据
	r.ParseForm()

	filesha1 := r.Form.Get("filehash")

	fMeta := meta.GetFileMeta(filesha1)
	// 这里可能还存在删除失败的情况
	os.Remove(fMeta.Location)

	// 删除数据
	meta.RemoveFileMeta(filesha1)

	w.WriteHeader(http.StatusOK)
}

